-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handling central text overflow and adding tooltip for donut charts #26192
base: master
Are you sure you want to change the base?
Conversation
📊 Bundle size report🤖 This report was generated against d445789f33a50255a6a51548d7282a482bc78a0b |
Asset size changesSize Auditor did not detect a change in bundle size for any component! Baseline commit: d445789f33a50255a6a51548d7282a482bc78a0b (build) |
@microsoft-github-policy-service agree [company="Microsoft"]
|
@microsoft-github-policy-service agree company="Microsoft"
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 8ce67c1:
|
@@ -80,6 +111,59 @@ export class Arc extends React.Component<IArcProps, IArcState> { | |||
wrapTextInsideDonut(classNames.insideDonutString, this.props.innerRadius! * 2 - TEXT_PADDING); | |||
} | |||
|
|||
private _getTruncatedText(text: string, maxWidth: number): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this work along with wrapTextInsideDonut, if wrapping and truncation both are enabled on the same text
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrapTextInsideDonut ensures that the words are placed in a new line if the current line length exceeds the maximum allowable width inside the donut. However, if a single word itself (even when placed in a new line) exceeds the maxWidth, it does not truncate the word. Thus, _getTruncatedText() determines if truncation is required for a single word or a group of words when inner text exceeds the maximum allowable width.
textAnchor={'middle'} | ||
className={this._classNames.insideDonutString} | ||
y={5} | ||
id={'Donut_center_text'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont hardcode ids
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the wrapping utility has dependency on this Id, need to keep this Id hardcoded
|
||
while ((word = words.pop()!)) { | ||
line.push(word); | ||
tspan.text(line.join(' ') + ' '); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are truncating here without wrapping. First try to solve via wrapping and then by truncation. And lets discuss what all possible scenarios are there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made the change to wrap first and then truncate. Added 7 corner cases too that were handled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case 5 and case 6, you mentioned you are truncating vertically and horizontally, but you are truncating it only once. Can you clarify on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Atishay, this change is now outdated and I have moved it to the utilities.ts in wrapTextInsideDonut(). After wrapping, if the text exceeds either horizontally (exceeds the central width) or vertically (if it goes beyond the inner circle vertically), then we are adding the ellipsis and breaking. If we want to continue truncation even after the text is truncated one way, we will need to add ellipsis both horizontally and vertically might not be a good experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment is on the description that you have provided for case 5 and case 6. Here you are truncating only once and not both ways. Can you update the description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated description.
c35dd98
to
42d2636
Compare
position: 'absolute', | ||
textAlign: 'center', | ||
top: '0px', | ||
background: theme.semanticColors.bodyBackground, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a default theme always available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, the theme is coming as a prop from the parent component, which is a required property
} | ||
// Determine if truncation is required vertically | ||
// If truncation is not required vertically, append a new line while taking care of horizontal truncation | ||
if (tspan.node()!.getBoundingClientRect().y < maxWidth) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldnt this be maxHeight
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since maxHeight is the same as the maxWidth, I have reused maxWidth
line = [word]; | ||
tspanEllipsis.text(word); | ||
|
||
// Determine if truncation is appending the text exceeds maximum width vertically or horizontally |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by verticl width
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant the vertical height, changing the sentence
03d8453
to
66c1fa7
Compare
…ers/srmukher/DonutBugFix
this._tooltip = document.getElementById(this._tooltipId); | ||
if (this._tooltip) { | ||
this._tooltip!.innerHTML = text.toString(); | ||
this._tooltip!.style.display = 'block'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be done using react states and not direct dom interactions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
packages/react-charting/src/components/DonutChart/Arc/Arc.types.ts
Outdated
Show resolved
Hide resolved
// Determine if truncation is required vertically | ||
// If yes, remove the last line, append ellipsis | ||
// to the last word of the previous line (truncate horizontally) and break | ||
if (tspan.node()!.getBoundingClientRect().y > maxWidth && line.length >= 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be max height?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since maxHeight is the same as the maxWidth, I have reused maxWidth
// Truncate till the text fits in the maximum allowable width | ||
while (tspan.node()!.getComputedTextLength() > maxWidth - ellipsisLength) { | ||
lastWord = line.pop()!; | ||
if (lastWord !== undefined && lastWord.length >= truncateCount) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't break happen only if last word is undefined. Is second part of the expression also relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes this is relevant because if the (tspan.node()!.getComputedTextLength() > maxWidth - ellipsisLength) and the lastword.length is < truncateCount, popping just the last word is sufficient after which it will append the ellipsis
@@ -118,11 +123,23 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
> | |||
<FocusZone direction={FocusZoneDirection.horizontal} handleTabKey={FocusZoneTabbableElements.all}> | |||
<div> | |||
<div |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use SvgTooltipText control here.
@@ -80,6 +80,7 @@ export class Pie extends React.Component<IPieProps, {}> { | |||
{this.props.valueInsideDonut && ( | |||
<text | |||
y={5} | |||
id="Donut_center_text" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use getId?
@@ -25,6 +26,10 @@ export class Arc extends React.Component<IArcProps, IArcState> { | |||
return null; | |||
} | |||
|
|||
public constructor(props: IArcProps) { | |||
super(props); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change needed?
@@ -1351,11 +1351,12 @@ export function rotateXAxisLabels(rotateLabelProps: IRotateLabelProps) { | |||
return Math.floor(maxHeight / 1.414); // Compute maxHeight/tanInverse(45) to get the vertical height of labels. | |||
} | |||
|
|||
export function wrapTextInsideDonut(selectorClass: string, maxWidth: number) { | |||
export function wrapTextInsideDonut(selectorClass: string, maxWidth: number, val: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this val is needed as there is only 1 text element inside donut
.text(word); | ||
// append a line only if the line is within the max height | ||
// else truncate horizontally and break | ||
if (tspan.node()!.getBoundingClientRect().y + lineLength < maxWidth) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are you handling the case when text inside donut is "Is in is in in is in"
if (tspan.node()!.getComputedTextLength() > maxWidth && line.length > 1) { | ||
// Determine if wrapping is required | ||
// If yes, append a new line only if it does not exceed the max height | ||
// here, max width = max height, so maxWidth is reused to check max height |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does max height account for the curved shape. The available space for top line/bottom line is less than the middle line.
Or is this scenario too complex to handle.
@srmukher Reviving this PR. Could you merge master and update this PR for review. |
Handling central text overflow and adding tooltip for donut charts.
Work item: 5321
Previous Behavior
Previous Behavior Example 1:

Previous Behavior Example 2:

New Behavior
New Behavior Example 1:

New Behavior Example 2:

Corner cases handled:
Case 1: Text is small : eg: "39,000" => No wrapping or truncation required

Case 2: Text is small but words are long (Single line). eg: "Incomprehensibilities" => Truncate horizontally, no wrapping required

Case 3: Text is small but words are long (Multi line) : "Tasks are incomprehensible" => Wrap and then truncate horizontally

Case 4: Text is long but words are small (Single line). eg: "It is done" => No wrapping or truncation required

Case 5: Text is long but words are small (Multi line) : eg: "Handling central text overflow and adding tooltip for donut charts" => After wrapping, if the text exceeds either horizontally (exceeds the central width) or vertically (if it goes beyond the inner circle vertically), then add the ellipsis (truncating) and break.

Case 6: Text and words both are long. eg: "Incomprehensibilities happen for texts that are not truncated" => After wrapping, if the text exceeds either horizontally (exceeds the central width) or vertically (if it goes beyond the inner circle vertically), then add the ellipsis (truncating) and break.

Case 7: Other languages (Japanese) single line: "やるべきことが残っている" => Truncate horizontally

Case 8: Other languages (Japanese) multi line: eg: "53 やるべきことが残っている" => Wrap and then Truncate horizontally

Related Issue(s)
https://uifabric.visualstudio.com/iss/_workitems/edit/5321/